using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Wolfpack.Periscope.Core.Extensions;
using Wolfpack.Periscope.Core.Interfaces;
using Wolfpack.Periscope.Core.Interfaces.Entities;

namespace Wolfpack.Periscope.Core.Renderers
{
    public class DefaultPanelRenderer : IPanelRenderer
    {
        private readonly IDashboardInfrastructure _infrastructure;
        private readonly IEnumerable<IWidgetRenderer> _widgetRenderers;

        public DefaultPanelRenderer(IDashboardInfrastructure infrastructure,
            IEnumerable<IWidgetRenderer> widgetRenderers)
        {
            _infrastructure = infrastructure;
            _widgetRenderers = widgetRenderers;
        }

        public RenderedPanel Render(IDashboardPanel panel)
        {
            var renderedWidgets = RenderWidgets(panel.Widgets).ToList();

            return new RenderedPanel
            {
                DwellInSeconds = panel.DwellInSeconds,
                Id = panel.Id,
                Name = panel.Name,
                Sequence = panel.Sequence,
                Version = panel.Version,
                Widgets = renderedWidgets,
                Scripts = renderedWidgets.SelectMany(w => w.Scripts)
                   .Concat(_infrastructure.Scripts)
                   .Distinct((a, b) => a.Name.Equals(b.Name, StringComparison.OrdinalIgnoreCase))
                   .ToList(),
                StyleSheets = renderedWidgets.SelectMany(w => w.StyleSheets)
                                .Distinct((a, b) => a.Name.Equals(b.Name, StringComparison.OrdinalIgnoreCase))
                                .ToList()
            };
        }

        private IEnumerable<RenderedWidget> RenderWidgets(IEnumerable<WidgetInstance> widgets)
        {
            var renderedWidgets = new ConcurrentBag<RenderedWidget>();

            Parallel.ForEach(widgets, instance =>
            {
                var renderer = _widgetRenderers.SingleOrDefault(x => x.Id.Equals(instance.RendererId));
                if (renderer == null)
                    throw new InvalidOperationException(string.Format("Widget Renderer '{0}' does not exist!",
                        instance.RendererId));

                var renderedWidget = renderer.Render(instance);
                renderedWidgets.Add(renderedWidget);
            });

            return renderedWidgets;
        }
    }
}